﻿using System;
using System.Net.Http;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using DotnetApp.Security;
using IdentityModel.Client;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;

namespace DotnetApp.Services
{
    public class DataGatewayHttpClient
    {
        private const string TokenCacheKey = "access_token";
        private readonly HttpClient _httpClient;
        private readonly DataGatewayOptions _options;
        private readonly IMemoryCache _cache;
        private readonly IHashHelper _hashHelper;
        private readonly ITextProtector _textProtector;
        private readonly JsonSerializerOptions _jsonOptions;

        public DataGatewayHttpClient(HttpClient httpClient, 
            IOptions<DataGatewayOptions> options, 
            IMemoryCache cache,
            IHashHelper hashHelper,
            ITextProtector textProtector, 
            JsonSerializerOptions jsonOptions)
        {
            _httpClient = httpClient;
            _cache = cache;
            _hashHelper = hashHelper;
            _textProtector = textProtector;
            _jsonOptions = jsonOptions;
            _options = options.Value;
        }

        private async ValueTask<string> GetToken()
        {
            if (_cache.TryGetValue<string>(TokenCacheKey, out var token))
            {
                return token;
            }

            var identityAddress = _options.Host + "/oauth";

#if DEV
            identityAddress = "http://localhost:9000"; // 开发调试
#endif

            var disco = await _httpClient.GetDiscoveryDocumentAsync(identityAddress);
            if (disco.IsError)
            {
                throw new HttpRequestException(disco.Error);
            }
            
            var tokenResponse = await _httpClient.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
            {
                Address = disco.TokenEndpoint,
                ClientId = _options.ClientId,
                ClientSecret = _options.ClientSecret,
                Scope = _options.Scope
            });

            if (tokenResponse.IsError)
            {
                throw new HttpRequestException(tokenResponse.Error);
            }

            token = tokenResponse.AccessToken;

            _cache.Set(TokenCacheKey, token, absoluteExpirationRelativeToNow: TimeSpan.FromSeconds(tokenResponse.ExpiresIn - 50));

            return token;
        }
        
        public async Task<string> SendAsync(object data)
        {
            // 获取Token
            var token = await GetToken();

            // 序列化
            var json = JsonSerializer.Serialize(data, _jsonOptions);
            // 加密
            var encrypted = _textProtector.Encrypt(json, _options.AesKey);
            // 签名
            var signature = _hashHelper.Sha1(encrypted + _options.HashKey);
            
            var content = new StringContent(encrypted, Encoding.UTF8, "application/sha1+json");
            content.Headers.Add("x-signature", signature);
            var url = _options.Host + "/data/api/v1/message";
#if DEV
            url = "http://localhost:5000/api/v1/message"; // 开发调试
#endif
            _httpClient.SetBearerToken(token);
            var response = await _httpClient.PostAsync(url, content);
            response.EnsureSuccessStatusCode();
            var result = await response.Content.ReadAsStringAsync();
            return result;
        }
    }
}